/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package com.dotexe.webframework.core;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Properties;
import java.util.UUID;

import javax.servlet.http.HttpSession;

import org.json.JSONException;

import com.dotexe.webframework.core.clientactions.StartUseCaseConfiguration;
import com.dotexe.webframework.core.communication.CallBackResultCollection;
import com.dotexe.webframework.core.datasources.DataSource;
import com.dotexe.webframework.core.eventargs.RefreshStateArgs;

/**
*
* @author jovan.tomasevic
*/
public class ApplicationController {

   private static String basePackageName;
   private static final String CONFIG_FILENAME = "dotexe-webframwork.properties";
   static {
       try {
           InputStream propsInputStream = ApplicationController.class.getClassLoader().getResourceAsStream( CONFIG_FILENAME );
           Properties properties = new Properties();
           properties.load( propsInputStream );

           basePackageName = properties.getProperty( "BASE_PACKAGE_NAME" );
       } catch ( IOException ex ) {
           throw  new RuntimeException("Could not read dotexe config file [" + CONFIG_FILENAME + "]");
       }

   }
   /**
    * need to be fixed
    */
   private List<String> excludeFromActions = new ArrayList<String>();
   String excludeTextChanged = "TextChanged";
   String excludeCheckedChanged = "CheckedChanged";

   private void initExcludeFromActions() {
       this.excludeFromActions.add( excludeTextChanged );
       this.excludeFromActions.add( excludeCheckedChanged );
   }
   /** <summary>
    * Gets or sets the element id.
    * </summary>
    * <value>The element id.</value>
    */
   public String elementId;
   /** <summary>
    * Usecase that coused client - server interaction
    * </summary>
    */
   public ClientApplication currrentUsecase;
   /** <summary>
    * Session cache of usecase instances
    * </summary>
    */
   public ClientApplicationCollection apps;
   /** <summary>
    * Default use case start configuration
    * </summary>
    */
   public StartUseCaseConfiguration defaultUseCaseConfiguration;
   /** <summary>
    * Default use case
    * </summary>
    */
   public ClientApplication defaultUseCase;
   /** <summary>
    * Gets the Controller cache.
    * </summary>
    * <value>The cache.</value>
    */
   public HashMap<String, Object> cache;

   public ApplicationController() {
       this.cache = new HashMap<String, Object>();
       this.apps = new ClientApplicationCollection();
       this.elementId = UUID.randomUUID().toString();
       // need to fix
       initExcludeFromActions();
   }
   // get singlton instance. one object per session

   public static ApplicationController getInstance() throws Exception {
       HttpSession session = Session.session;
       if ( session == null ) {
           return null;
       }
       ApplicationController controlor = (ApplicationController) session.getAttribute( "ApplicationControlorInstance" );
       return controlor;
   }
   // set singlton object. one object per session

   public static void setInstance( ApplicationController controlor ) {
       HttpSession session = Session.session;
       session.setAttribute( "ApplicationControlorInstance", controlor );
   }

   public CallBackResultCollection refreshState( RefreshStateArgs args ) throws ClassNotFoundException, InstantiationException, IllegalAccessException, JSONException, NoSuchFieldException, NoSuchMethodException, IllegalArgumentException, InvocationTargetException, ParseException, Exception {
       if ( args.isInit == null ) {
           args.isInit = false;
       }
       if ( args.isInit ) {
           ClientApplication app = startUseCase( args );
       }
       updateSources( args );
       // TODO: there is more if that is necessery. fix it! jovan.tomasevic@dotexe.rs
       if ( !args.isInit ) {
           startActions( args );
       }
       return this.processResults();
   }

   private ClientApplication startUseCase( RefreshStateArgs args ) throws ClassNotFoundException, InstantiationException, IllegalAccessException,
                                                                          NoSuchFieldException, JSONException, NoSuchMethodException, InvocationTargetException, Exception {
       ClientApplication app = getUseCase( args );
       app.controller = this;
       app.start();
       //if(!(args.elementID == null || args.elementID.isEmpty())){
       //app.elementID = args.elementID;
       //app.getConfig().setElementID(args.elementID);
       //}
       //if(!(args.holderID == null || args.holderID.isEmpty())){
       //app.holderID = args.holderID;
       //app.getConfig().setHolderID(args.holderID);
       //}
       apps.add( app );
       return app;
   }

   private ClientApplication getUseCase( RefreshStateArgs args ) throws ClassNotFoundException, InstantiationException, IllegalAccessException, JSONException, NoSuchFieldException, Exception {
       ClientApplication usecase = apps.get( args.applicationName );
       if ( usecase == null ) {
           usecase = CreateUseCase( args );
       }

       if ( !usecase.isLoaded ) {
           usecase.load();
       }

       return usecase;
   }

   private ClientApplication CreateUseCase( RefreshStateArgs args ) throws ClassNotFoundException, InstantiationException, IllegalAccessException, JSONException, NoSuchFieldException {
       Class c = Class.forName( basePackageName + args.applicationName );

       ClientApplication app = (ClientApplication) c.newInstance();
       app.name = args.applicationName;
       app.elementID = args.elementID;
       app.holderID = args.holderID;
//       StartUseCaseConfiguration config = new StartUseCaseConfiguration();
//       config.setApplicationName (args.applicationName);
//       config.setElementID(app.elementID);
//       config.setHolderID(app.holderID);
//       app.clientActions.add(config);
//       apps.add(app);

       return app;
   }
   

   public void CreateUseCase( StartUseCaseConfiguration start ) throws JSONException, InstantiationException, IllegalAccessException, ClassNotFoundException, Exception {
       Class c = Class.forName( ApplicationController.basePackageName + start.getApplicationName() );
       ClientApplication app = (ClientApplication) c.newInstance();
       app.name = start.getApplicationName();
       app.clientActions.add( start );
       apps.add( app );
       app.start();
   }

   public CallBackResultCollection processResults() throws JSONException {
       CallBackResultCollection results = new CallBackResultCollection();
       for ( int i = 0; i < apps.size(); i++ ) {
           ClientApplication app = apps.get( i );
           app.prepareResponse();
           if ( !app.getResponse().isEmpty() ) {
               results.put( app.getResponse() );
           }
       }
       return results;
   }

   public void clearResponse() throws JSONException {
       for ( int i = 0; i < apps.size(); i++ ) {
           apps.get( i ).clearRespones();
       }
   }

   private void updateSources( RefreshStateArgs args )
           throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException, JSONException, NoSuchMethodException, IllegalArgumentException, InvocationTargetException, ParseException {
       ClientApplication application = apps.get( args.elementID );
       for ( int i = 0; i < args.dataUpdates.size(); i++ ) {
           DataSource source = application.getDataSources().get( args.dataUpdates.get( i ).dataSourceName );
           source.update( args, args.dataUpdates.get( i ) );
       }
       if ( args.dataSourceName != null ) {
           DataSource s = application.getDataSources().get( args.dataSourceName );
           if ( s != null ) {
               s.update( args, null );
           }
       }
   }

   private void startActions( RefreshStateArgs args ) throws NoSuchMethodException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
       if ( this.excludeFromActions.contains( args.clientOperationName ) ) {
           return;
       }
       if ( args.clientOperationName != null && !args.clientOperationName.isEmpty() ) {
           ClientApplication application = apps.get( args.elementID );
           //application.CallMethod(args);

           application.invokeTrigger( args );
       }
   }
}

